<template>
  <div class="personTrack"></div>
</template>
<script setup lang="ts">
import { useViewportStore } from '@/store/viewport'
import { useAuthStore } from '@/store/auth'
import { storeToRefs } from 'pinia'
import { gsap } from 'gsap'
import { CSS2DObject, GLTFLoader } from 'three/examples/jsm/Addons.js'
import { shallowRef, onMounted, createVNode, render, onUnmounted } from 'vue'
import { AnimationMixer, CatmullRomCurve3, Clock, Vector3 } from 'three'
import { getPath } from '@/utils/getPath'
import { findClosestT, findTrackPath, mergeCurvesSequentially } from '@/utils/findTrackPath'
import BTrack1 from '@/components/b-track1.vue'
import useAnimationFrame from '@/hooks/useAnimationFrame'
import { getAlarmPeopleByAreaId, getPersonTrackData } from '@/api/person'
import emiter from '@/utils/mitt'
import { useFocusObject } from '@/utils/useFocusObject'

const viewportStore = useViewportStore()
const { scene, curveNetwork, baseVec, publicGroup, orbitControls, camera } =
  storeToRefs(viewportStore)
const authStore = useAuthStore()
const { globalParams } = storeToRefs(authStore)
const { focusObject } = useFocusObject()

const peopleList = shallowRef([])
const offset = new Vector3(0, 500, -500)
onMounted(async () => {
  focusObject(publicGroup.value)
  camera.value.lookAt(publicGroup.value.position)
  setTimeout(() => {
    const vuc3 = orbitControls.value.target.clone().sub(offset)
    gsap.to(orbitControls.value.target, { duration: 0.5, ...vuc3 })
    camera.value.lookAt(vuc3)
  }, 1000)
  const { data } = await getAlarmPeopleByAreaId()
  peopleList.value = data
  peopleList.value.forEach(async (people) => {
    const { data } = await getPersonTrackData(
      globalParams.value.startTime,
      people.peopleNo,
      globalParams.value.endTime
    )
    let dataFilters = []
    for (let i = 0; i < data.length; i++) {
      const item = data[i]
      item.jzPoint = window.jzPoints[item.jzNo]
      if (!item.jzPoint) {
        console.warn('未找到数据', item) // 打印警告信息
        continue // 跳过当前循环
      }
      const road = getPath(item.jzPoint)
      if (!road) {
        console.warn('未找到道路', item) // 打印警告信息
        continue // 跳过当前循环
      }
      item.curve = road.geometry.parameters.path
      item.uuid = road.uuid
      dataFilters.push(item)
    }
    people.trackData = dataFilters
    const { scene: model, animations } = await loadGLTFModel()
    model.scale.set(3, 3, 3)
    people.peopleModel = model
    scene.value?.add(people.peopleModel) // 添加模型到场景中
    people.mixer = new AnimationMixer(people.peopleModel)
    people.clip = people.mixer.clipAction(animations[0])

    people.label = createdTracklLabel(people.name)
    people.peopleModel.add(people.label)

    people.timeline = gsap.timeline() // 创建时间线
    try {
      const arr = mergeCurvesSequentially(people.trackData)
      const resultPath = findTrackPath(
        arr,
        curveNetwork.value.graph,
        curveNetwork.value.connectionMap
      )
      people.peopleModel.position.copy(resultPath[0].path[0]).sub(baseVec.value)

      for (let i = 0; i < resultPath.length; i++) {
        if (resultPath[i].path.length < 2) {
          people.timeline.to(null, {
            duration: resultPath[i].time
          })
          continue
        }
        const curve = new CatmullRomCurve3(resultPath[i].path)
        const tA = findClosestT(curve, resultPath[i].path[0])
        const tB = findClosestT(curve, resultPath[i].path[resultPath[i].path.length - 1])
        const obj = { t: tA } // 中间变量存储当前t值

        people.timeline.to(obj, {
          t: tB,
          duration: resultPath[i].time,
          ease: 'linear',
          onUpdate: () => {
            // 根据当前t值更新物体位置
            const position = curve.getPoint(obj.t).sub(baseVec.value)
            people.peopleModel.position.copy(position)

            const tangent = curve.getTangent(obj.t)
            people.peopleModel.lookAt(position.clone().add(tangent))
          }
        })
        people.timeline.pause()
      }
    } catch (error) {
      console.warn('findContinuousPath error', error)
    }
  })
})
emiter.on('timePlay', (isEnd) => {
  peopleList.value.forEach((people) => {
    if (isEnd) {
      people.timeline.progress(0)
    }
    people.timeline.play()
    people.clip.play()
  })
})
emiter.on('timePause', () => {
  peopleList.value.forEach((people) => {
    people.timeline.pause()
    people.clip.stop()
  })
})
emiter.on('timeSeek', (progress) => {
  peopleList.value.forEach((people) => {
    people.timeline.progress(progress / 100)
  })
})

emiter.on('changeScene', () => {
  peopleList.value.forEach((people) => {
    people.timeline.progress(0)
  })
})

onUnmounted(() => {
  peopleList.value.forEach((people) => {
    people.peopleModel.remove(people.label)
    people.peopleModel.removeFromParent()
  })

  emiter.off('timePlay')
  emiter.off('timePause')
  emiter.off('timeSeek')
})

const loader = new GLTFLoader()
const BASE_URL = import.meta.env.BASE_URL
function loadGLTFModel() {
  return new Promise((resolve, reject) => {
    loader.load(
      BASE_URL + 'model/people.glb',
      (gltf) => {
        resolve(gltf)
      },
      undefined,
      (error) => {
        reject(error)
      }
    )
  })
}

function createdTracklLabel(text) {
  const container = document.createElement('div')
  const vm = createVNode(
    BTrack1,
    {
      name: text
    },
    null
  )
  render(vm, container)
  const label = new CSS2DObject(container.firstElementChild as Node)
  label.Vnode = vm
  return label
}

const clock = new Clock()
useAnimationFrame(() => {
  const delta = clock.getDelta()
  peopleList.value.forEach((people) => {
    people.mixer?.update(delta)
  })
})
</script>
<style lang="scss" scoped></style>
